library(Seurat)
library(conos)
Loading required package: igraph

Attaching package: ‘igraph’

The following object is masked from ‘package:scater’:

    normalize

The following objects are masked from ‘package:DelayedArray’:

    path, simplify

The following objects are masked from ‘package:dplyr’:

    as_data_frame, groups, union

The following objects are masked from ‘package:purrr’:

    compose, simplify

The following object is masked from ‘package:tidyr’:

    crossing

The following object is masked from ‘package:tibble’:

    as_data_frame

The following object is masked from ‘package:GenomicRanges’:

    union

The following object is masked from ‘package:IRanges’:

    union

The following object is masked from ‘package:S4Vectors’:

    union

The following objects are masked from ‘package:BiocGenerics’:

    normalize, path, union

The following objects are masked from ‘package:stats’:

    decompose, spectrum

The following object is masked from ‘package:base’:

    union
library(ggpubr)
library(tidyverse)
library(SingleCellExperiment)
# library(monocle3)
source("~/multiOmic_benchmark/utils.R")
source("~/multiOmic_benchmark/integrateBenchmark.R")

Based on the results of my benchmark, I set out to align expression and accessibility profiles from the F74 developing thymus dataset to detect changes in accessibility along pseudotime trajectories. While the benchmark was based on the task of label propagation, I here use the two most faithful methods (Seurat CCA and Conos) to achieve a common embedding of ATAC-seq and RNA-seq cells.

Load datasets.

rna.sce <- readRDS("~/my_data/F74_RNA_seurat_processed.RDS")
atac.sce <- readRDS("~/my_data/F74_ATAC_snapAtac_processed_bgmat.RDS")

Filter genes with zero variance

rna.gene.var <- as.matrix(counts(rna.sce)) %>% rowVars()
atac.gene.var <- as.matrix(counts(atac.sce)) %>% rowVars()

rna.sce <- rna.sce[which(rna.gene.var > 0),]
atac.sce <- atac.sce[which(atac.gene.var > 0),]

rna.sce; atac.sce
class: SingleCellExperiment 
dim: 24510 8321 
metadata(0):
assays(3): counts cpm logcounts
rownames(24510): RP11-34P13.3 RP11-34P13.7 ... AC233755.1
  AC240274.1
rowData names(0):
colnames(8321): AAACCTGAGTTCGATC_1 AAACCTGCAAGTTGTC_1 ...
  TTTGTCAAGCTGAACG_2 TTTGTCAGTATTAGCC_2
colData names(1): annotation
reducedDimNames(0):
spikeNames(0):
class: SingleCellExperiment 
dim: 31122 5793 
metadata(0):
assays(3): counts cpm logcounts
rownames(31122): A1BG A1BG-AS1 ... ZYX ZZEF1
rowData names(0):
colnames(5793): AAACGAAAGTGAACCG-1 AAACGAACATCGGCCA-1 ...
  TTTGTGTTCGATCGCG-1 TTTGTGTTCTGAGTAC-1
colData names(28): orig.ident nCount_ATAC ... nFeature_ACTIVITY
  ident
reducedDimNames(2): LSI UMAP
spikeNames(0):

Integration of T cells clusters

I re-run the integration based on the T cell subset. To select cells from the scATAC dataset, I take the SnapATAC clusters that best correspond to T-cells, based on label transfer.

tcells.sce.atac <- atac.sce[,which(as.numeric(atac.sce$seurat_clusters) %in% c(1:9))]

tcells.rna.ix <- which(rna.sce$annotation %in% c("DN","DP (Q)", "DP (P)", "SP (1)", "SP (2)"))
tcells.sce.rna <- rna.sce[,tcells.rna.ix]

tcells.sce.list <- list(RNA=tcells.sce.rna, ATAC=tcells.sce.atac)

Next, I select genes on which to perform integration. I take the union of the most variable features in the RNA dataset and the most covered features in the ATAC dataset

Remove cell cycle genes, that might interfere with pseudotime ordering

# integrate_features_ref <- hvg.rna 
# integrate_features_ref <- setdiff(integrate_features_ref, cell_cycle_genes) 
liger.obj <- scaleNotCenter(liger.obj, remove.missing = F)
Error in intI(i, n = x@Dim[1], dn[[1]], give.dn = FALSE) : 
  invalid character indexing

Visualize T cells in RNA dataset

Visualize markers

Visualize T cells in ATAC dataset: is the trajectory visible in the binary matrix?

Run CCA and Conos

Run CCA

coembed <- merge(x = seurat.list$RNA, y = seurat.list$ATAC)

coembed <- ScaleData(coembed, features = integrate_features_union, do.scale = FALSE)
Centering data matrix

  |                                                                                                          
  |                                                                                                    |   0%
  |                                                                                                          
  |==============                                                                                      |  14%
  |                                                                                                          
  |=============================                                                                       |  29%
  |                                                                                                          
  |===========================================                                                         |  43%
  |                                                                                                          
  |=========================================================                                           |  57%
  |                                                                                                          
  |=======================================================================                             |  71%
  |                                                                                                          
  |======================================================================================              |  86%
  |                                                                                                          
  |====================================================================================================| 100%
coembed <- RunPCA(coembed, features = integrate_features_union, verbose = FALSE)
coembed <- RunUMAP(coembed, dims = 1:30)
10:53:47 UMAP embedding parameters a = 0.9922 b = 1.112
10:53:47 Read 12078 rows and found 30 numeric columns
10:53:47 Using Annoy for neighbor search, n_neighbors = 30
10:53:47 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:53:50 Writing NN index file to temp file /tmp/RtmplOAmA4/file511c460e48ef
10:53:50 Searching Annoy index using 1 thread, search_k = 3000
10:53:55 Annoy recall = 100%
10:53:59 Commencing smooth kNN distance calibration using 1 thread
10:54:03 Initializing from normalized Laplacian + noise
10:54:03 Commencing optimization for 200 epochs, with 562314 positive edges
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:54:17 Optimization finished
coembed <- AddMetaData(coembed, metadata = ifelse(colnames(coembed) %in% colnames(seurat.list[[reference]]), reference, query), col.name = "tech")

DimPlot(coembed, group.by = c('tech', "annotation"))

Run Conos

FeaturePlot(coembed, features = t.cell.markers$known.markers, split.by = "tech", slot = "data", cols = viridis::viridis(n=100))

Transfer labels on ATAC dataset

FeaturePlot(coembed, features = "prediction.score.max", cells = which(coembed$tech=="ATAC")) + scale_color_viridis_c()
Scale for 'colour' is already present. Adding another scale for 'colour', which will replace the existing scale.

Run Pseudotime analysis

Identify cell of origin based on IGLL1 and CD34

AddMetaData(coembed, ifelse(colnames(coembed)==cell.oo, TRUE, FALSE), col.name = "iroot_cell")
An object of class Seurat 
55632 features across 12078 samples within 2 assays 
Active assay: RNA (24510 features)
 1 other assay present: ATAC
 2 dimensional reductions calculated: pca, umap
merged.sce <- SingleCellExperiment(list(counts=coembed@assays$RNA@counts, logcounts=coembed@assays$RNA@data), colData=coembed@meta.data[, c("annotation", "tech", "iroot_cell")],
                     reducedDims = map(coembed@reductions, ~ .x@cell.embeddings))

saveRDS(object = merged.sce, "~/my_data/Tcells_CCA_integration_20191127.RDS")
saveRDS(object = integrate_features_union, "~/my_data/intFeatures_Tcells_CCA_integration_20191127.RDS")
dpt <- read.csv('~/my_data/Tcells_CCA_integration_20191127_scanpy_dpt.csv') %>%
  select(X, dpt_pseudotime)

coembed <- AddMetaData(coembed, column_to_rownames(dpt, 'X'))

Check expression of markers along pseudotime

Bin ATAC cells by pseudotime

Fraction of accessible bins at each pseudotime bin

Motif analysis

Call peaks

peaks.ls = mclapply(seq(clusters.sel), function(i){
  print(paste("cluster", clusters.sel[i]))
  peaks = runMACS(
      obj=snap.out[which(snap.out@metaData$barcode %in% colnames(tcells.sce.atac)[tcells.sce.atac$seurat_clusters==clusters.sel[i]]),], 
      output.prefix=paste0("Tcells_F74_", gsub("cluster", clusters.sel)[i]),
      path.to.snaptools="/opt/conda/bin/snaptools",
      path.to.macs="/opt/conda/bin/macs2",
      gsize="hs", # mm, hs, etc
      buffer.size=500, 
      num.cores=3,
      macs.options="--nomodel --shift 100 --ext 200 --qval 5e-2 -B --SPMR",
      tmp.folder=tempdir()
 )
peaks
}, mc.cores=5)
all scheduled cores encountered errors in user code

Using chromVAR implementation in snapATAC

# snap.out = makeBinary(x.sp, "pmat")
snap.out@mmat = runChromVAR(
  obj=snap.out[which(snap.out@metaData$barcode %in% dpt.bin.cells),],
  input.mat="bmat",
  genome=BSgenome.Hsapiens.UCSC.hg38,
  # min.count=10,
  species="Homo sapiens"
);
Epoch: checking depedent packages ... 
Epoch: checking input parameters ... 
Epoch: creating chromVAR object ... 
Epoch: computing GC bias ... 
Epoch: getting JASPAR motifs ... 


There are 1 result in use. The connection will be released when they are closed

LS0tCnRpdGxlOiAiUHNldWRvdGltZSBhbmFseXNpcyBvZiBULWNlbGxzIGluIGRldmVsb3BpbmcgdGh5bXVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoY29ub3MpCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKIyBsaWJyYXJ5KG1vbm9jbGUzKQpzb3VyY2UoIn4vbXVsdGlPbWljX2JlbmNobWFyay91dGlscy5SIikKc291cmNlKCJ+L211bHRpT21pY19iZW5jaG1hcmsvaW50ZWdyYXRlQmVuY2htYXJrLlIiKQpzb3VyY2UoIn4vbXVsdGlPbWljX2JlbmNobWFyay9wcmVwcm9jZXNzL3NlbGVjdEZlYXR1cmVzLlIiKQpgYGAKCkJhc2VkIG9uIHRoZSByZXN1bHRzIG9mIG15IGJlbmNobWFyaywgSSBzZXQgb3V0IHRvIGFsaWduIGV4cHJlc3Npb24gYW5kIGFjY2Vzc2liaWxpdHkgcHJvZmlsZXMgZnJvbSB0aGUgRjc0IGRldmVsb3BpbmcgdGh5bXVzIGRhdGFzZXQgdG8gZGV0ZWN0IGNoYW5nZXMgaW4gYWNjZXNzaWJpbGl0eSBhbG9uZyBwc2V1ZG90aW1lIHRyYWplY3Rvcmllcy4gV2hpbGUgdGhlIGJlbmNobWFyayB3YXMgYmFzZWQgb24gdGhlIHRhc2sgb2YgbGFiZWwgcHJvcGFnYXRpb24sIEkgaGVyZSB1c2UgdGhlIHR3byBtb3N0IGZhaXRoZnVsIG1ldGhvZHMgKFNldXJhdCBDQ0EgYW5kIENvbm9zKSB0byBhY2hpZXZlIGEgY29tbW9uIGVtYmVkZGluZyBvZiBBVEFDLXNlcSBhbmQgUk5BLXNlcSBjZWxscy4KCkxvYWQgZGF0YXNldHMuCgpgYGB7cn0Kcm5hLnNjZSA8LSByZWFkUkRTKCJ+L215X2RhdGEvRjc0X1JOQV9zZXVyYXRfcHJvY2Vzc2VkLlJEUyIpCmF0YWMuc2NlIDwtIHJlYWRSRFMoIn4vbXlfZGF0YS9GNzRfQVRBQ19zbmFwQXRhY19wcm9jZXNzZWRfYmdtYXQuUkRTIikKYGBgCgpGaWx0ZXIgZ2VuZXMgd2l0aCB6ZXJvIHZhcmlhbmNlCmBgYHtyfQpybmEuZ2VuZS52YXIgPC0gYXMubWF0cml4KGNvdW50cyhybmEuc2NlKSkgJT4lIHJvd1ZhcnMoKQphdGFjLmdlbmUudmFyIDwtIGFzLm1hdHJpeChjb3VudHMoYXRhYy5zY2UpKSAlPiUgcm93VmFycygpCgpybmEuc2NlIDwtIHJuYS5zY2Vbd2hpY2gocm5hLmdlbmUudmFyID4gMCksXQphdGFjLnNjZSA8LSBhdGFjLnNjZVt3aGljaChhdGFjLmdlbmUudmFyID4gMCksXQoKcm5hLnNjZTsgYXRhYy5zY2UKYGBgCgoKIyMgSW50ZWdyYXRpb24gb2YgVCBjZWxscyBjbHVzdGVycwpJIHJlLXJ1biB0aGUgaW50ZWdyYXRpb24gYmFzZWQgb24gdGhlIFQgY2VsbCBzdWJzZXQuIFRvIHNlbGVjdCBjZWxscyBmcm9tIHRoZSBzY0FUQUMgZGF0YXNldCwgSSB0YWtlIHRoZSBTbmFwQVRBQyBjbHVzdGVycyB0aGF0IGJlc3QgY29ycmVzcG9uZCB0byBULWNlbGxzLCBiYXNlZCBvbiBsYWJlbCB0cmFuc2Zlci4KCmBgYHtyfQp0Y2VsbHMuc2NlLmF0YWMgPC0gYXRhYy5zY2VbLHdoaWNoKGFzLm51bWVyaWMoYXRhYy5zY2Ukc2V1cmF0X2NsdXN0ZXJzKSAlaW4lIGMoMTo5KSldCgp0Y2VsbHMucm5hLml4IDwtIHdoaWNoKHJuYS5zY2UkYW5ub3RhdGlvbiAlaW4lIGMoIkROIiwiRFAgKFEpIiwgIkRQIChQKSIsICJTUCAoMSkiLCAiU1AgKDIpIikpCnRjZWxscy5zY2Uucm5hIDwtIHJuYS5zY2VbLHRjZWxscy5ybmEuaXhdCgp0Y2VsbHMuc2NlLmxpc3QgPC0gbGlzdChSTkE9dGNlbGxzLnNjZS5ybmEsIEFUQUM9dGNlbGxzLnNjZS5hdGFjKQpgYGAKCk5leHQsIEkgc2VsZWN0IGdlbmVzIG9uIHdoaWNoIHRvIHBlcmZvcm0gaW50ZWdyYXRpb24uIEkgdGFrZSB0aGUgdW5pb24gb2YgdGhlIG1vc3QgdmFyaWFibGUgZmVhdHVyZXMgaW4gdGhlIFJOQSBkYXRhc2V0IGFuZCB0aGUgbW9zdCBjb3ZlcmVkIGZlYXR1cmVzIGluIHRoZSBBVEFDIGRhdGFzZXQKCmBgYHtyfQpoY2cuYXRhYyA8LSBzZWxlY3RfaGlnaGx5Q292ZXJlZCh0Y2VsbHMuc2NlLmxpc3QkQVRBQywgZnJhY19jZWxscyA9IDAuMikKaHZnLnJuYSA8LSBzZWxlY3RfaGlnaGx5VmFyaWFibGUodGNlbGxzLnNjZS5saXN0JFJOQSkKClVwU2V0Ujo6dXBzZXQoVXBTZXRSOjpmcm9tTGlzdChsaXN0KEhWRy5STkE9aHZnLnJuYSwgSENHLkFUQUM9aGNnLmF0YWMpKSkKYGBgCgpSZW1vdmUgY2VsbCBjeWNsZSBnZW5lcywgdGhhdCBtaWdodCBpbnRlcmZlcmUgd2l0aCBwc2V1ZG90aW1lIG9yZGVyaW5nCmBgYHtyfQpjZWxsX2N5Y2xlX2dlbmVzIDwtIHJlYWQudGFibGUoIn4vYW5ub3RhdGlvbnMvY2VsbF9jeWNsZV9nZW5lcy50c3YiKSRWMQoKaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIDwtIHVuaW9uKGh2Zy5ybmEsIGhjZy5hdGFjKQppbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24gPC0gc2V0ZGlmZihpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGNlbGxfY3ljbGVfZ2VuZXMpIAoKIyMgU2VsZWN0IGZlYXR1cmVzIGluIGJvdGggZGF0YXNldHMKaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uIDwtIGludGVyc2VjdChpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGludGVyc2VjdChyb3duYW1lcyh0Y2VsbHMuc2NlLmxpc3QkQVRBQyksIHJvd25hbWVzKHRjZWxscy5zY2UubGlzdCRSTkEpKSkgCgpsZW5ndGgoaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uKQojIGludGVncmF0ZV9mZWF0dXJlc19yZWYgPC0gaHZnLnJuYSAKIyBpbnRlZ3JhdGVfZmVhdHVyZXNfcmVmIDwtIHNldGRpZmYoaW50ZWdyYXRlX2ZlYXR1cmVzX3JlZiwgY2VsbF9jeWNsZV9nZW5lcykgCmxpZ2VyLm9iaiA8LSBzY2FsZU5vdENlbnRlcihsaWdlci5vYmosIHJlbW92ZS5taXNzaW5nID0gRikKYGBgCgpWaXN1YWxpemUgVCBjZWxscyBpbiBSTkEgZGF0YXNldApgYGB7cn0KdGNlbGxzLlJOQS51bmlvbiA8LSB0Y2VsbHMuc2V1Lmxpc3QkUk5BClZhcmlhYmxlRmVhdHVyZXModGNlbGxzLlJOQS51bmlvbikgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uCnRjZWxscy5STkEudW5pb24gPC0gU2NhbGVEYXRhKHRjZWxscy5STkEudW5pb24pICU+JSBSdW5QQ0EoKSAlPiUgUnVuVU1BUChkaW1zPTE6NDApCgpEaW1QbG90KHRjZWxscy5STkEudW5pb24sIGdyb3VwLmJ5ID0gImFubm90YXRpb24iLCBsYWJlbD1UUlVFKSArIGdndGl0bGUoIlJOQSAtIGZlYXR1cmUgdW5pb24iKQpgYGAKClZpc3VhbGl6ZSBtYXJrZXJzIApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTEwfQp0LmNlbGwubWFya2VycyA8LSBsaXN0KGtub3duLm1hcmtlcnMgPSBjKCJDRDM0IiwgIklHTEwxIiwgIlRSR0MyIiwgIlRSREMiLCAiUFRDUkEiLCAiVFJCQzIiLCAiVFJBQyIsICJDRDQiLCAiQ0Q4QSIsICJDRDhCIiksCiAgICAgICAgICAgICAgICAgICAgICAgY2hlbW9raW5lLnJlY2VwdG9ycyA9IGMoIkNDUjkiLCAiQ0NSNyIpLAogICAgICAgICAgICAgICAgICAgICAgIHRjci5hY3RpdmF0aW9uID0gYygiQ0Q1IiwgIkNEMjciKSwKICAgICAgICAgICAgICAgICAgICAgICBwcm9saWZlcmF0aW9uPWMoIlBDTkEiLCAiQ0RLMSIsICJNS0k2NyIpLAogICAgICAgICAgICAgICAgICAgICAgIGN5Y2xpbi5EID0gYygiQ0NORDIiLCAiQ0NORDMiKSwKICAgICAgICAgICAgICAgICAgICAgICByZWNvbWJpbmF0aW9uPWMoIlJBRzEiLCAiUkFHMiIpLAogICAgICAgICAgICAgICAgICAgICAgIGFwb3B0b3Npcz1jKCJIUksiLCJCTUYiLCAiVFA1M0lOUDEiKSwKICAgICAgICAgICAgICAgICAgICAgICBzdGFnZS5tYXJrZXJzID0gYygiU1QxOCIsICJISVZFUDMiLCAiUkdQRDMiLCAiU01QRDMiLCAiQVFQMyIsICJST1JDIiwgIlNBVEIxIiwgIlRPWDIiKQogICAgICAgICAgICAgICAgICAgICAgICkgCiMgRmVhdHVyZVBsb3QodGNlbGxzLlJOQS5yZWYsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCkZlYXR1cmVQbG90KHRjZWxscy5STkEudW5pb24sIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMCkpCmBgYAoKVmlzdWFsaXplIFQgY2VsbHMgaW4gQVRBQyBkYXRhc2V0OiBpcyB0aGUgdHJhamVjdG9yeSB2aXNpYmxlIGluIHRoZSBiaW5hcnkgbWF0cml4PwpgYGB7cn0KdGNlbGxzLkFUQUMudW5pb24gPC0gdGNlbGxzLnNldS5saXN0JEFUQUMKIyB0Y2VsbHMuQVRBQy51bmlvbiA8LSBOb3JtYWxpemVEYXRhKHRjZWxscy5BVEFDLnVuaW9uKQpWYXJpYWJsZUZlYXR1cmVzKHRjZWxscy5BVEFDLnVuaW9uKSA8LSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24KdGNlbGxzLkFUQUMudW5pb24gPC0gUnVuTFNJKHRjZWxscy5BVEFDLnVuaW9uLCBuPTUwLCBzY2FsZS5tYXggPSBOVUxMKQp0Y2VsbHMuQVRBQy51bmlvbiA8LSBSdW5VTUFQKHRjZWxscy5BVEFDLnVuaW9uLCByZWR1Y3Rpb24gPSAibHNpIiwgZGltcyA9IDE6NTApCgpEaW1QbG90KHRjZWxscy5BVEFDLnVuaW9uLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIsIGxhYmVsID0gVFJVRSkgKyBnZ3RpdGxlKCJBVEFDIGdtYXQiKQpgYGAKCiMjIyMgUnVuIENDQSBhbmQgQ29ub3MKClJ1biBDQ0EKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD01fQpzY2UubGlzdCA8LSB0Y2VsbHMuc2NlLmxpc3QKcmVmZXJlbmNlID0gIlJOQSIKcXVlcnkgPSAiQVRBQyIgCnNldXJhdC5saXN0IDwtIGltYXAoc2NlLmxpc3QsIH4gYXMuU2V1cmF0KC54LCBhc3NheT0ueSkpCnNldXJhdC5saXN0IDwtIGltYXAoc2V1cmF0Lmxpc3QsIH4gUmVuYW1lQ2VsbHMoLngsIGFkZC5jZWxsLmlkPS55KSkKIyMgU2NhbGUgZGF0YQpzZXVyYXQubGlzdCA8LSBtYXAoc2V1cmF0Lmxpc3QsIH4gU2NhbGVEYXRhKC54KSkKIyMgQ2FsY3VsYXRlIENDQSBhbmNob3JzCnRyYW5zZmVyLmFuY2hvcnMgPC0gRmluZFRyYW5zZmVyQW5jaG9ycyhyZWZlcmVuY2UgPSBzZXVyYXQubGlzdFtbcmVmZXJlbmNlXV0sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcXVlcnkgPSBzZXVyYXQubGlzdFtbcXVlcnldXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJjY2EiKQoKIyMgSW1wdXRlIGV4cHJlc3Npb24gcHJvZmlsZXMgZm9yIEFUQUMgY2VsbHMgKGZvciBhbGwgZ2VuZXMsIG5vdCBqdXN0IGludGVncmF0aW9uIGZlYXR1cmVzKQpyZWZkYXRhIDwtIEdldEFzc2F5RGF0YShzZXVyYXQubGlzdCRSTkEsIGFzc2F5ID0gIlJOQSIsIHNsb3QgPSAiZGF0YSIpCmltcHV0YXRpb24gPC0gVHJhbnNmZXJEYXRhKGFuY2hvcnNldCA9IHRyYW5zZmVyLmFuY2hvcnMsIHJlZmRhdGEgPSByZWZkYXRhLCB3ZWlnaHQucmVkdWN0aW9uID0gc2V1cmF0Lmxpc3QkQVRBQ1tbIkxTSSJdXSkKCiMjIE1lcmdlIGRhdGFzZXRzIGFuZCBjby1lbWJlZApzZXVyYXQubGlzdCRBVEFDW1siUk5BIl1dIDwtIGltcHV0YXRpb24KY29lbWJlZCA8LSBtZXJnZSh4ID0gc2V1cmF0Lmxpc3QkUk5BLCB5ID0gc2V1cmF0Lmxpc3QkQVRBQykKCmNvZW1iZWQgPC0gU2NhbGVEYXRhKGNvZW1iZWQsIGZlYXR1cmVzID0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLCBkby5zY2FsZSA9IEZBTFNFKQpjb2VtYmVkIDwtIFJ1blBDQShjb2VtYmVkLCBmZWF0dXJlcyA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgdmVyYm9zZSA9IEZBTFNFKQpjb2VtYmVkIDwtIFJ1blVNQVAoY29lbWJlZCwgZGltcyA9IDE6MzApCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIG1ldGFkYXRhID0gaWZlbHNlKGNvbG5hbWVzKGNvZW1iZWQpICVpbiUgY29sbmFtZXMoc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dKSwgcmVmZXJlbmNlLCBxdWVyeSksIGNvbC5uYW1lID0gInRlY2giKQoKRGltUGxvdChjb2VtYmVkLCBncm91cC5ieSA9IGMoJ3RlY2gnLCAiYW5ub3RhdGlvbiIpKQpgYGAKClJ1biBDb25vcwpgYGB7cn0KZGF0YS5wcm9jZXNzZWQgPC0gbWFwKHNjZS5saXN0LCB+IGFzLlNldXJhdCgueCkpIApWYXJpYWJsZUZlYXR1cmVzKGRhdGEucHJvY2Vzc2VkW1tyZWZlcmVuY2VdXSkgPC0gaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uClZhcmlhYmxlRmVhdHVyZXMoZGF0YS5wcm9jZXNzZWRbW3F1ZXJ5XV0pIDwtIGludGVncmF0ZV9mZWF0dXJlc191bmlvbgpkYXRhLnByb2Nlc3NlZCA8LSBtYXAoZGF0YS5wcm9jZXNzZWQsIH4gU2NhbGVEYXRhKC54KSAlPiUgUnVuUENBKGRpbXM9MTozMCkpCmwuY29uIDwtIENvbm9zJG5ldyhkYXRhLnByb2Nlc3NlZCxuLmNvcmVzPTMwKQpsLmNvbiRidWlsZEdyYXBoKGs9MTUsay5zZWxmPTUsay5zZWxmLndlaWdoPTAuMDEsbmNvbXBzPTMwLG4ub2RnZW5lcz01ZTMsc3BhY2U9J1BDQScpIAoKbC5jb24kZmluZENvbW11bml0aWVzKHJlc29sdXRpb249MS41KQpsLmNvbiRlbWJlZEdyYXBoKGFscGhhPTEvMikKICAKY29ub3Mub3V0IDwtIGNvbm9zLm1vZGVsJG1vZGVsCmwuY29uJHBsb3RHcmFwaChjb2xvci5ieSA9ICJzYW1wbGUiKQoKZ2VuZVggPC0gc2V1cmF0Lmxpc3RbW3JlZmVyZW5jZV1dQGFzc2F5cyRSTkFAc2NhbGUuZGF0YVszLF0KZ2VuZVggPC0gc2V0TmFtZXMoYW5ub3RhdGlvblssMV0sIHJvd25hbWVzKGFubm90YXRpb24pKQpuZXcubGFiZWwucHJvYmFiaWxpdGllcyA8LSBsLmNvbiRwcm9wYWdhdGVMYWJlbHMobGFiZWxzID0gZ2VuZVgsIHZlcmJvc2UgPSBULCBmaXhlZC5pbml0aWFsLmxhYmVscz1UKQpoaXN0KG5ldy5sYWJlbC5wcm9iYWJpbGl0aWVzKQpsLmNvbiRjb3JyZWN0R2VuZXMoZ2VuZXMgPSBpbnRlZ3JhdGVfZmVhdHVyZXNfdW5pb24sIGNvdW50Lm1hdHJpeCA9IE1hdHJpeChzZXVyYXQubGlzdCRBVEFDQGFzc2F5cyRBVEFDQGRhdGEpKQoKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0yMCwgZmlnLndpZHRoPTl9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMkc3RhZ2UubWFya2Vycywgc3BsaXQuYnkgPSAidGVjaCIsIGNvbHMgPSB2aXJpZGlzOjp2aXJpZGlzKG49MTAwKSkgKyBnZ3RpdGxlKCJTdGFnZSBNYXJrZXJzIikKYGBgCmBgYHtyLCBmaWcuaGVpZ2h0PTI1LCBmaWcud2lkdGg9OX0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSB0LmNlbGwubWFya2VycyRrbm93bi5tYXJrZXJzLCBzcGxpdC5ieSA9ICJ0ZWNoIiwgc2xvdCA9ICJkYXRhIiwgY29scyA9IHZpcmlkaXM6OnZpcmlkaXMobj0xMDApKQpgYGAKYGBge3J9CkZlYXR1cmVQbG90KGNvZW1iZWQsIGZlYXR1cmVzID0gdC5jZWxsLm1hcmtlcnMkcmVjb21iaW5hdGlvbiwgc3BsaXQuYnkgPSAidGVjaCIsIHNsb3QgPSAiZGF0YSIsIGNvbHMgPSB2aXJpZGlzOjp2aXJpZGlzKG49MTAwKSkgCmBgYAoKVHJhbnNmZXIgbGFiZWxzIG9uIEFUQUMgZGF0YXNldApgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTV9CmNlbGx0eXBlLnByZWRpY3Rpb25zIDwtIFRyYW5zZmVyRGF0YShhbmNob3JzZXQgPSB0cmFuc2Zlci5hbmNob3JzLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZmRhdGEgPSBzZXVyYXQubGlzdFtbcmVmZXJlbmNlXV0kYW5ub3RhdGlvbiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB3ZWlnaHQucmVkdWN0aW9uID0gc2V1cmF0Lmxpc3QkQVRBQ1tbIkxTSSJdXSkKY2VsbC50eXBlcyA8LSB1bmlxdWUoY29lbWJlZCRhbm5vdGF0aW9uKQpjZWxsLnR5cGUucGFsIDwtIGJyZXdlcl9wYWxldHRlXzRfdmFsdWVzKGNlbGwudHlwZXMsIHBhbGV0dGUgPSAiU2V0MSIpICU+JSBzZXROYW1lcyhjZWxsLnR5cGVzKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBtZXRhZGF0YSA9IGNlbGx0eXBlLnByZWRpY3Rpb25zKQpjb2VtYmVkQG1ldGEuZGF0YSAlPD4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCkgJT4lCiAgZHBseXI6Om11dGF0ZShhbm5vdGF0aW9uPWlmZWxzZShpcy5uYShwcmVkaWN0ZWQuaWQpICwgYW5ub3RhdGlvbiwgTkEpKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoKQoKY29lbWJlZEBtZXRhLmRhdGEgPC0KICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oKSAlPiUKICBkcGx5cjo6bXV0YXRlKGFubm90YXRpb249aWZlbHNlKGlzLm5hKGFubm90YXRpb24pICYgcHJlZGljdGlvbi5zY29yZS5tYXggPiAwLjUsIHByZWRpY3RlZC5pZCwgYW5ub3RhdGlvbikpICU+JQogIGNvbHVtbl90b19yb3duYW1lcygpCkNvbWJpbmVQbG90cygKICBsaXN0KERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJwcmVkaWN0ZWQuaWQiKSwgY29scyA9IGNlbGwudHlwZS5wYWwpICsgZ2d0aXRsZSgicHJlZGljdGlvbiIpLAogIERpbVBsb3QoY29lbWJlZCwgZ3JvdXAuYnkgPSBjKCJhbm5vdGF0aW9uIiksIGNvbHMgPSBjZWxsLnR5cGUucGFsKSArIGdndGl0bGUoIk9yaWdpbmFsICsgcHJlZGljdGlvbiIpKSwKICBsZWdlbmQgPSAidG9wIgogICkKYGBgCmBgYHtyfQpGZWF0dXJlUGxvdChjb2VtYmVkLCBmZWF0dXJlcyA9ICJwcmVkaWN0aW9uLnNjb3JlLm1heCIsIGNlbGxzID0gd2hpY2goY29lbWJlZCR0ZWNoPT0iQVRBQyIpKSArIHNjYWxlX2NvbG9yX3ZpcmlkaXNfYygpCmBgYAoKCiMjIyBSdW4gUHNldWRvdGltZSBhbmFseXNpcyAKSWRlbnRpZnkgY2VsbCBvZiBvcmlnaW4gYmFzZWQgb24gSUdMTDEgYW5kIENEMzQKYGBge3IsIGZpZy5oZWlnaHQ9MTYsIGZpZy53aWR0aD0xNn0KRmVhdHVyZVBsb3QoY29lbWJlZCwgZmVhdHVyZXMgPSBjKCJJR0xMMSIsICJDRDM0IiksIHNwbGl0LmJ5ID0gInRlY2giLCBzbG90ID0gImRhdGEiLCBjb2xzID0gdmlyaWRpczo6dmlyaWRpcyhuPTEwMCkpCmBgYApgYGB7cn0KY2VsbC5vbyA8LQogIGNvZW1iZWRAbWV0YS5kYXRhICU+JSAKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBtdXRhdGUoSUdMTDE9Y29lbWJlZEBhc3NheXMkUk5BQGNvdW50c1siSUdMTDEiLGNlbGxdKSAlPiUKICBzZWxlY3QoY2VsbCwgYW5ub3RhdGlvbiwgSUdMTDEpICU+JQogIGFycmFuZ2UoLUlHTEwxKSAlPiUKICBmaWx0ZXIoYW5ub3RhdGlvbj09IkROIikgJT4lCiAgdG9wX24oMSwgSUdMTDEpICU+JQogIHB1bGwoY2VsbCkKCmNvZW1iZWRAcmVkdWN0aW9ucyR1bWFwQGNlbGwuZW1iZWRkaW5ncyAlPiUKICBhcy50aWJibGUocm93bmFtZXM9ImNlbGwiKSAlPiUKICBtdXRhdGUoY2VsbC5vbyA9IGlmZWxzZShjZWxsICVpbiUgY2VsbC5vbywgVCwgRikpICU+JQogIGdncGxvdChhZXMoVU1BUF8xLCBVTUFQXzIpKSArCiAgZ2VvbV9wb2ludChjb2xvcj0iZ3JleTUwIikgKwogIGdlb21fcG9pbnQoZGF0YT0uICU+JSBmaWx0ZXIoY2VsbC5vbyksY29sb3I9J3JlZCcpICsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YT0uICU+JSBmaWx0ZXIoY2VsbC5vbyksIGFlcyhsYWJlbD0iY2VsbCBvZiBvcmlnaW4iKSwgY29sb3I9J3JlZCcpICsKICB0aGVtZV9jb3dwbG90KCkgCgpjb2VtYmVkIDwtIEFkZE1ldGFEYXRhKGNvZW1iZWQsIGlmZWxzZShjb2xuYW1lcyhjb2VtYmVkKT09Y2VsbC5vbywgVFJVRSwgRkFMU0UpLCBjb2wubmFtZSA9ICJpcm9vdF9jZWxsIikKCiAgCmBgYAoKCmBgYHtyfQptZXJnZWQuc2NlIDwtIFNpbmdsZUNlbGxFeHBlcmltZW50KGxpc3QoY291bnRzPWNvZW1iZWRAYXNzYXlzJFJOQUBjb3VudHMsIGxvZ2NvdW50cz1jb2VtYmVkQGFzc2F5cyRSTkFAZGF0YSksIGNvbERhdGE9Y29lbWJlZEBtZXRhLmRhdGFbLCBjKCJhbm5vdGF0aW9uIiwgInRlY2giLCAiaXJvb3RfY2VsbCIpXSwKICAgICAgICAgICAgICAgICAgICAgcmVkdWNlZERpbXMgPSBtYXAoY29lbWJlZEByZWR1Y3Rpb25zLCB+IC54QGNlbGwuZW1iZWRkaW5ncykpCgpzYXZlUkRTKG9iamVjdCA9IG1lcmdlZC5zY2UsICJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl8yMDE5MTEyNy5SRFMiKQpzYXZlUkRTKG9iamVjdCA9IGludGVncmF0ZV9mZWF0dXJlc191bmlvbiwgIn4vbXlfZGF0YS9pbnRGZWF0dXJlc19UY2VsbHNfQ0NBX2ludGVncmF0aW9uXzIwMTkxMTI3LlJEUyIpCmBgYAoKCmBgYHtyfQpkcHQgPC0gcmVhZC5jc3YoJ34vbXlfZGF0YS9UY2VsbHNfQ0NBX2ludGVncmF0aW9uXzIwMTkxMTI3X3NjYW5weV9kcHQuY3N2JykgJT4lCiAgc2VsZWN0KFgsIGRwdF9wc2V1ZG90aW1lKQoKY29lbWJlZCA8LSBBZGRNZXRhRGF0YShjb2VtYmVkLCBjb2x1bW5fdG9fcm93bmFtZXMoZHB0LCAnWCcpKQpzYXZlUkRTKGNvZW1iZWQsICJ+L215X2RhdGEvVGNlbGxzX0NDQV9pbnRlZ3JhdGlvbl9zZXVyYXRfMjAxOTExMjcuUm1kIikKYGBgCgpgYGB7cn0KRmVhdHVyZVBsb3QoY29lbWJlZCwgcmVkdWN0aW9uID0gInVtYXAiLCBmZWF0dXJlID0gImRwdF9wc2V1ZG90aW1lIiwgc3BsaXQuYnkgPSAidGVjaCIsIGNvbD12aXJpZGlzOjp2aXJpZGlzKDEwKSkgCmBgYApgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CmNvZW1iZWRAbWV0YS5kYXRhICU+JQogIGRwbHlyOjptdXRhdGUoYERQVCByYW5rYD1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiAgZ2dwbG90KGFlcyhgRFBUIHJhbmtgKSkgKwogICMgZ2dwbG90KGFlcyhkcHRfcHNldWRvdGltZSkpICsKICAjIGdlb21fcG9pbnQoYWVzKGNvbG9yPWFubm90YXRpb24pKSArCiAgIyBmYWNldF9ncmlkKGFubm90YXRpb25+LikKICBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbD1hbm5vdGF0aW9uKSwgYmlucz01MCkgKwogIGZhY2V0X2dyaWQoYW5ub3RhdGlvbn50ZWNoKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjZWxsLnR5cGUucGFsKQoKYGBgCgpDaGVjayBleHByZXNzaW9uIG9mIG1hcmtlcnMgYWxvbmcgcHNldWRvdGltZQpgYGB7cn0KY29lbWJlZEBhc3NheXMkUk5BQGRhdGFbdC5jZWxsLm1hcmtlcnMka25vd24ubWFya2VycywgXSAlPiUKICBhcy5tYXRyaXgoKSAlPiUKICByZXNoYXBlMjo6bWVsdCh2YXJuYW1lcz1jKCJnZW5lIiwgImNlbGwiKSkgJT4lCiAgZ3JvdXBfYnkoZ2VuZSkgJT4lCiAgIyBtdXRhdGUodmFsdWU9KHZhbHVlLW1lYW4odmFsdWUpKS9zZCh2YWx1ZSkpICU+JQogIGxlZnRfam9pbihjb2VtYmVkQG1ldGEuZGF0YVssImRwdF9wc2V1ZG90aW1lIiwgZHJvcD1GXSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCJjZWxsIikpICU+JQogIG11dGF0ZShwc2V1ZG90aW1lLnJhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIGdncGxvdChhZXMocHNldWRvdGltZS5yYW5rLCBnZW5lKSkgKwogIGdlb21fdGlsZShhZXMoZmlsbD12YWx1ZSkpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYygpCmBgYAoKQmluIEFUQUMgY2VsbHMgYnkgcHNldWRvdGltZQpgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTR9CmRwdC5kZiA8LSAKICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICAjIGZpbHRlcih0ZWNoPT0iQVRBQyIpICU+JQogICMgZ3JvdXBfYnkodGVjaCkgJT4lCiAgZHBseXI6Om11dGF0ZShkcHRfcmFuaz1kZW5zZV9yYW5rKGRwdF9wc2V1ZG90aW1lKSkgJT4lCiAgbXV0YXRlKGRwdF9iaW49Y3V0KGRwdF9yYW5rLCBicmVha3MgPSAxMDApKSAlPiUKICBtdXRhdGUoZHB0X2Jpbj1hcy5udW1lcmljKGRwdF9iaW4pKSAlPiUKICAjIHVuZ3JvdXAoKSAlPiUKICBzZWxlY3QoY2VsbCx0ZWNoLCBhbm5vdGF0aW9uLCBwcmVkaWN0aW9uLnNjb3JlLm1heCwgZHB0X2JpbiwgZHB0X3BzZXVkb3RpbWUpCgpjZWxsLnR5cGUucGwgPC0gZHB0LmRmICU+JQogIGdncGxvdChhZXMoZHB0X2JpbiwgZmlsbCA9IGFubm90YXRpb24pKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgZmFjZXRfZ3JpZCh0ZWNofi4pICsKICB4bGFiKCJQc2V1ZG90aW1lIGJpbiIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikKCmNlbGwudHlwZS5wbApgYGAKCkZyYWN0aW9uIG9mIGFjY2Vzc2libGUgYmlucyBhdCBlYWNoIHBzZXVkb3RpbWUgYmluIApgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTh9CnNuYXAub3V0IDwtIHJlYWRSRFMoZmlsZSA9ICJ+L215X2RhdGEvY2VsbHJhbmdlci1hdGFjMTEwX2NvdW50XzMwNDM5X1dTU1M4MDM4MzYwX0dSQ2gzOC0xXzFfMC5zbmFwQVRBQy5SRFMiKQphdGFjLmRwdC5kZiA8LSAKICBjb2VtYmVkQG1ldGEuZGF0YSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBmaWx0ZXIodGVjaD09IkFUQUMiKSAlPiUKICAjIGdyb3VwX2J5KHRlY2gpICU+JQogIGRwbHlyOjptdXRhdGUoZHB0X3Jhbms9ZGVuc2VfcmFuayhkcHRfcHNldWRvdGltZSkpICU+JQogIG11dGF0ZShkcHRfYmluPWN1dChkcHRfcmFuaywgYnJlYWtzID0gMTAwKSkgJT4lCiAgbXV0YXRlKGRwdF9iaW49YXMubnVtZXJpYyhkcHRfYmluKSkgJT4lCiAgIyB1bmdyb3VwKCkgJT4lCiAgc2VsZWN0KGNlbGwsdGVjaCwgYW5ub3RhdGlvbiwgcHJlZGljdGlvbi5zY29yZS5tYXgsIGRwdF9iaW4sIGRwdF9wc2V1ZG90aW1lKQoKY2VsbC50eXBlLnBsIDwtIGF0YWMuZHB0LmRmICU+JQogIGdncGxvdChhZXMoZHB0X2JpbiwgZmlsbCA9IGFubm90YXRpb24pKSArCiAgZ2VvbV9iYXIoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgeGxhYigiUHNldWRvdGltZSBiaW4iKSArCiAgdGhlbWVfYncoYmFzZV9zaXplID0gMTYpCgpncm91cHMgPC0gYXRhYy5kcHQuZGZbLCBjKCJjZWxsIiwgImRwdF9iaW4iKV0KYm1hdCA8LSBzbmFwLm91dEBibWF0W3N0cl9yZW1vdmUoZ3JvdXBzJGNlbGwsICJBVEFDXyIpLF0KZnJhYy5hY2Nlc3NpYmxlIDwtIHJvd1N1bXMoYm1hdCkvbmNvbChibWF0KQphY2MuZnJhY3Rpb24ucGwgPC0gZ3JvdXBzICU+JQogIG11dGF0ZShmcmFjX2FjY2Vzc2libGU9ZnJhYy5hY2Nlc3NpYmxlW3N0cl9yZW1vdmUoY2VsbCwgIkFUQUNfIildKSAlPiUKICBnZ3Bsb3QoYWVzKGRwdF9iaW4sIGZyYWNfYWNjZXNzaWJsZSkpICsKICBnZW9tX2JveHBsb3QoYWVzKGdyb3VwPWFzLmZhY3RvcihkcHRfYmluKSksIG91dGxpZXIuYWxwaGEgPSAwLjMpICsKICAjIGdlb21faml0dGVyKGFscGhhPTAuMSkgKwogIHhsYWIoIlBzZXVkb3RpbWUgYmluIikgKwogIHlsYWIoIkZyYWN0aW9uIG9mIGFjY2Vzc2libGUgYmlucyIpICsKICB0aGVtZV9idyhiYXNlX3NpemUgPSAxNikgCiAgCmFjYy5mcmFjdGlvbi5wbCAKcGxvdF9ncmlkKGNlbGwudHlwZS5wbCArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0idG9wIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSwgCiAgICAgICAgICBhY2MuZnJhY3Rpb24ucGwsIAogICAgICAgICAgYWxpZ24gPSAidiIsIG5jb2w9MSwgbnJvdz0yLCBheGlzPSJsIikKYGBgCgoKCmBgYHtyfQphY2MubWF0IDwtIGNvZW1iZWRAYXNzYXlzJEFUQUNAZGF0YQptYXJrZXJzLmFjYyA8LSBhY2MubWF0W2ludGVyc2VjdChjKHQuY2VsbC5tYXJrZXJzJGtub3duLm1hcmtlcnMsIHQuY2VsbC5tYXJrZXJzJGNoZW1va2luZS5yZWNlcHRvcnMsIHQuY2VsbC5tYXJrZXJzJHJlY29tYmluYXRpb24pLCByb3duYW1lcyhhY2MubWF0KSksLCBkcm9wPUZdCgptYXJrZXJzLmRmIDwtIGRhdGEuZnJhbWUodChhcy5tYXRyaXgobWFya2Vycy5hY2NbLGRwdC5kZiRjZWxsW2RwdC5kZiR0ZWNoPT0iQVRBQyJdXSkpKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImNlbGwiKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IHJvd25hbWVzKG1hcmtlcnMuYWNjKSwgbmFtZXNfdG8gPSAibWFya2VyLmdlbmUiLCB2YWx1ZXNfdG8gPSAiYWNjZXNzaWJpbGl0eSIpCgphbm5vdGF0aW9uLmhtIDwtIGF0YWMuZHB0LmRmICU+JQogIGdyb3VwX2J5KGRwdF9iaW4sIGFubm90YXRpb24pICU+JQogIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBhbm5vdGF0aW9uKSkgKwogIGdlb21fdGlsZShhZXMoYWxwaGE9biwgZmlsbD1hbm5vdGF0aW9uKSkgICsKICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDE2KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWNlbGwudHlwZS5wYWwsIG5hLnZhbHVlPSJncmV5NTAiKSArCiAgZ3VpZGVzKGZpbGw9J25vbmUnLCBhbHBoYT0nbm9uZScpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpCgptYXJrZXJzLmhtIDwtIGF0YWMuZHB0LmRmICU+JQogIGZ1bGxfam9pbihtYXJrZXJzLmRmKSAlPiUKICBncm91cF9ieShkcHRfYmluLCBtYXJrZXIuZ2VuZSkgJT4lCiAgc3VtbWFyaXNlKGZyYWNfYWNjZXNzaWJsZT1zdW0oYWNjZXNzaWJpbGl0eSkvbigpKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgbXV0YXRlKG1hcmtlci5nZW5lPWZhY3RvcihtYXJrZXIuZ2VuZSwgbGV2ZWxzID0gYygiQ0QzNCIsICJJR0xMMSIsICJUUkdDMiIsICJUUkRDIiwgIlBUQ1JBIiwgIlRSQkMyIiwgIkNDUjkiLCJDQ1I3IiwgIlJBRzEiLCAiUkFHMiIsICJUUkFDIiwgIkNENCIsICJDRDhBIiwgIkNEOEIiKSkpICU+JQogIG11dGF0ZShtYXJrZXIuZ2VuZT1mYWN0b3IobWFya2VyLmdlbmUsIGxldmVscyA9IHJldihsZXZlbHMobWFya2VyLmdlbmUpKSkpICU+JQogIGdyb3VwX2J5KG1hcmtlci5nZW5lKSAlPiUKICBtdXRhdGUoZnJhY19hY2Nlc3NpYmxlPShmcmFjX2FjY2Vzc2libGUgLSBtaW4oZnJhY19hY2Nlc3NpYmxlKSkvbWF4KGZyYWNfYWNjZXNzaWJsZSkgLSBtaW4oZnJhY19hY2Nlc3NpYmxlKSkgJT4lCiAgZ2dwbG90KGFlcyhkcHRfYmluLCBtYXJrZXIuZ2VuZSwgZmlsbD1mcmFjX2FjY2Vzc2libGUpKSArIAogIGdlb21fdGlsZSgpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhuYW1lPSJGcmFjLmNlbGxzIikgKwogIHhsYWIoIlBzZXVkb3RpbWUgYmluIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTYpICsKICB0aGVtZShheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKQoKbGVnIDwtIGdldF9sZWdlbmQobWFya2Vycy5obSkKZ3IxIDwtIHBsb3RfZ3JpZChhbm5vdGF0aW9uLmhtLCBtYXJrZXJzLmhtICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwgbnJvdz0yLCByZWxfaGVpZ2h0cyA9IGMoMSwyKSwgYWxpZ24gPSAidiIpCmdyMiA8LSBwbG90X2dyaWQoZ2dwbG90KCkgKyAgdGhlbWVfdm9pZCgpLGxlZywgbnJvdz0yLCByZWxfaGVpZ2h0cyA9IGMoMSwyKSkKcGxvdF9ncmlkKGdyMSwgZ3IyLCByZWxfd2lkdGhzID0gYygzLDEpKQpgYGAKCiMjIE1vdGlmIGFuYWx5c2lzIAoKQ2FsbCBwZWFrcyAKYGBge3J9CiMjIENhbGwgcGVha3Mgb24gY2x1c3RlcnMKY2x1c3RlcnMuc2VsIDwtIHVuaXF1ZSh0Y2VsbHMuc2NlLmF0YWMkc2V1cmF0X2NsdXN0ZXJzKQpwZWFrcy5scyA9IG1jbGFwcGx5KHNlcShjbHVzdGVycy5zZWwpLCBmdW5jdGlvbihpKXsKICBwcmludChwYXN0ZSgiY2x1c3RlciIsIGNsdXN0ZXJzLnNlbFtpXSkpCiAgcGVha3MgPSBydW5NQUNTKAogICAgICBvYmo9c25hcC5vdXRbd2hpY2goc25hcC5vdXRAbWV0YURhdGEkYmFyY29kZSAlaW4lIGNvbG5hbWVzKHRjZWxscy5zY2UuYXRhYylbdGNlbGxzLnNjZS5hdGFjJHNldXJhdF9jbHVzdGVycz09Y2x1c3RlcnMuc2VsW2ldXSksXSwgCiAgICAgIG91dHB1dC5wcmVmaXg9cGFzdGUwKCJUY2VsbHNfRjc0X2NsdXN0ZXIiLCBjbHVzdGVycy5zZWxbaV0pLAogICAgICBwYXRoLnRvLnNuYXB0b29scz0iL29wdC9jb25kYS9iaW4vc25hcHRvb2xzIiwKICAgICAgcGF0aC50by5tYWNzPSIvb3B0L2NvbmRhL2Jpbi9tYWNzMiIsCiAgICAgIGdzaXplPSJocyIsICMgbW0sIGhzLCBldGMKICAgICAgYnVmZmVyLnNpemU9NTAwLCAKICAgICAgbnVtLmNvcmVzPTMsCiAgICAgIG1hY3Mub3B0aW9ucz0iLS1ub21vZGVsIC0tc2hpZnQgMTAwIC0tZXh0IDIwMCAtLXF2YWwgNWUtMiAtQiAtLVNQTVIiLAogICAgICB0bXAuZm9sZGVyPXRlbXBkaXIoKQogKQpwZWFrcwp9LCBtYy5jb3Jlcz01KQpwZWFrcy5uYW1lcyA9IHN5c3RlbSgibHMgfCBncmVwIG5hcnJvd1BlYWsiLCBpbnRlcm49VFJVRSkKcGVhay5nci5scyA9IGxhcHBseShwZWFrcy5uYW1lcywgZnVuY3Rpb24oeCl7CiAgcGVhay5kZiA9IHJlYWQudGFibGUoeCkKICBHUmFuZ2VzKHBlYWsuZGZbLDFdLCBJUmFuZ2VzKHBlYWsuZGZbLDJdLCBwZWFrLmRmWywzXSkpCn0pCnBlYWsuZ3IgPSByZWR1Y2UoUmVkdWNlKGMsIHBlYWsuZ3IubHMpKQoKIyMgTWFrZSBjZWxsIGJ5IHBlYWsgbWF0cml4CgpwZWFrcwpgYGAKClVzaW5nIGNocm9tVkFSIGltcGxlbWVudGF0aW9uIGluIHNuYXBBVEFDCmBgYHtyfQpsaWJyYXJ5KGNocm9tVkFSKQpsaWJyYXJ5KG1vdGlmbWF0Y2hyKQpsaWJyYXJ5KEJTZ2Vub21lLkhzYXBpZW5zLlVDU0MuaGczOCkKCgoKIyBzbmFwLm91dCA9IG1ha2VCaW5hcnkoeC5zcCwgInBtYXQiKQpzbmFwLm91dEBtbWF0ID0gcnVuQ2hyb21WQVIoCiAgb2JqPXNuYXAub3V0W3doaWNoKHNuYXAub3V0QG1ldGFEYXRhJGJhcmNvZGUgJWluJSBkcHQuYmluLmNlbGxzKSxdLAogIGlucHV0Lm1hdD0iYm1hdCIsCiAgZ2Vub21lPUJTZ2Vub21lLkhzYXBpZW5zLlVDU0MuaGczOCwKICAjIG1pbi5jb3VudD0xMCwKICBzcGVjaWVzPSJIb21vIHNhcGllbnMiCik7Cnguc3A7CmBgYAoKCmBgYHtyLCBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9N30KCmFjYy5kZiA8LSBkYXRhLmZyYW1lKHQoYXMubWF0cml4KGFjYy5tYXRbaW50ZWdyYXRlX2ZlYXR1cmVzX3VuaW9uLHN0cl9zdWJzZXQoY29sbmFtZXMoYWNjLm1hdCksIHBhdHRlcm4gPSAiQVRBQ18uKy0xIildKSkpICU+JQogIG11dGF0ZV9hbGwoYXMuZmFjdG9yKQoKdGVzdC5kZiA8LSBjYmluZChhY2MuZGZbLGludGVyc2VjdChodmcucm5hLCBjb2xuYW1lcyhhY2MuZGYpKSBdLCBkcHQ9ZHB0LmRmJGRwdF9wc2V1ZG90aW1lW2RwdC5kZiR0ZWNoPT0iQVRBQyJdKQpmaXQuZHB0IDwtIGxtKGRwdCB+IC4sIGRhdGE9dGVzdC5kZikKCmZpdC5jb2VmZnMgPC0gZml0LmRwdCRjb2VmZmljaWVudHNbMjpsZW5ndGgoZml0LmRwdCRjb2VmZmljaWVudHMpXQpoaXN0KGZpdC5jb2VmZnMsIGJyZWFrcyA9IDEwMCkKd2hpY2goYWJzKGZpdC5jb2VmZnMpID4gMC4xKQphbm92YS5kcHQgPC0gYW5vdmEoZml0LmRwdCkKYXNzb2NpYXRlZC5nZW5lcyA8LSByb3duYW1lcyhhbm92YS5kcHQpW3doaWNoKGFub3ZhLmRwdCRgUHIoPkYpYCA8IDAuMDEpXQoKYWNjLm1hdCAlPiUgZGltKCkKCmRwdC5iaW4uYWNjLm1hdCA8LSBhY2MubWF0W2Fzc29jaWF0ZWQuZ2VuZXMsIHN0cl9zdWJzZXQoY29sbmFtZXMoYWNjLm1hdCksIHBhdHRlcm4gPSAiQVRBQ18uKy0xIildICU+JQogIGFzLm1hdHJpeCgpICU+JQogIHJlc2hhcGUyOjptZWx0KHZhcm5hbWVzPWMoImdlbmUiLCAiY2VsbCIpKSAlPiUKICBsZWZ0X2pvaW4oZHB0LmRmKSAlPiUKICBncm91cF9ieShkcHRfYmluLCBnZW5lKSAlPiUKICBzdW1tYXJpc2UodmFsdWU9c3VtKHZhbHVlKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9ICJkcHRfYmluIiwgdmFsdWVzX2Zyb20gPSAidmFsdWUiKSAlPiUKICBjb2x1bW5fdG9fcm93bmFtZXMoJ2dlbmUnKSAlPiUKICBhcy5tYXRyaXgoKQoKcGhlYXRtYXA6OnBoZWF0bWFwKGRwdC5iaW4uYWNjLm1hdCAlPiUgdCgpICU+JSBzY2FsZSgpICU+JSB0KCksIGNsdXN0ZXJfY29scyA9IEYsIGNvbG9yID0gdmlyaWRpczo6dmlyaWRpcygxMCkpCiAgCgpnZ3Bsb3QoYWVzKGRwdF9iaW4sIGdlbmUsIGZpbGw9dmFsdWUpKSArCiAgZ2VvbV90aWxlKCkKYGBgCgoKCgoKCgoKCgoKCgo=